<?php

/**
 *
 * @author Antoine Rolland <antoine.rolland@keyconsulting.fr>
 *
 */
class Extracteur{

  const QUERY_BUG = "select p.name as application,
                            b.id as numero,
                            b.severity as severite,
                            b.resolution as resolution,
                            b.status as statut,
                            b.view_state as view_state,
                            b.summary as resume,
                            c.name as categorie,
                            b.priority as priorite,
                            b.fixed_in_version as version
                    from mantis_bug_table b
                    inner join mantis_project_table p on b.project_id = p.id
                    inner join mantis_category_table c on b.category_id = c.id
                    where b.project_id in
                       (SELECT p2.id
                        FROM `mantis_project_table` p2
                        INNER JOIN `mantis_project_hierarchy_table` h3 on h3.child_id = p2.id
                        INNER JOIN `mantis_project_hierarchy_table` h2 on h2.child_id = h3.parent_id
                        WHERE h2.parent_id = :projet_id)"; //Ne convient qu'à une organisation en sous sous projets

  const QUERY_HISTORIQUE = "select bug_id, date_modified, DATE(date_modified) as
                            date_sans_heure, old_value, new_value from mantis_bug_history_table where
                            bug_id = :bug_id and (type = 1 or field_name = 'status') order by date_modified";

  const QUERY_CRITERE = "select f.name as name, c.value as value from mantis_custom_field_string_table c,
                        mantis_custom_field_table f where c.field_id = f.id and bug_id = :bug_id";

  //Severite
  const CODE_SEVERITE_MINEUR = 50;
  const CODE_SEVERITE_MAJEUR = 60;
  const CODE_SEVERITE_BLOQUANT = 80;

  //Priorite
  const CODE_PRIORITE_BASSE = 20;
  const CODE_PRIORITE_NORMALE = 30;
  const CODE_PRIORITE_ELEVEE = 40;
  const CODE_PRIORITE_URGENTE = 50;

  //Statuts
  const CODE_STATUT_DETECTE = 30; //Etat "Accepté"
  const CODE_STATUT_RESOLU = 80;
  const CODE_STATUT_LIVRE = 85;
  const CODE_STATUT_FERME = 90;
  //Pas de tableau en constante
  public $CODE_STATUT_ATTENTE = array(20,25,60);

  //Mode de traitement, 1 EXECUTION, 2 DEGRADE (affichage des requetes) NON IMPLEMENTE
  const MODE_EXE = 1;
  const MODE_DEGRADE = 2;
  protected $mode = null;

  //PDO
  protected $dbh = null;


  /**
   * Construit l'extracteur en mode exe ou mode dégradé, initialise la connection
   *
   * @param $mantis_dsn, ex 'mysql:host=localhost;dbname=test'
   * @param $mantis_user
   * @param $mantis_pass
   * @param $mode, MODE_DEGRADE permet de ne pas exécuter mais de récupérer les requêtes à lancer (non actif)
   * @throws PDOException
   */
  public function __construct($mantis_dsn, $mantis_user, $mantis_pass, $mode = self::MODE_EXE)
  {
    $this->mode = $mode;
    $this->dbh = new PDO($mantis_dsn, $mantis_user, $mantis_pass);
  }

  /**
   * Extrait les bugs de le base mantis pour le projet donné
   *
   * @param integer $projet_mantis_id
   *           - actuellement, id Pole Metier
   *           - prévoir double boucle pour projet complet (TMA DR)
   *           - prévoir une simple égalité pour application unique
   * @throws sfException
   */
  public function extractBugs($projet_mantis_id)
  {
    $stmtBug = $this->dbh->prepare(self::QUERY_BUG);
    $stmtBug->bindParam(':projet_id', $projet_mantis_id);

    if ($stmtBug->execute()){
      while ($rowBug = $stmtBug->fetch()) {
        $bug = new Bug();
        $application = Doctrine::getTable('Application')->findOneByNomCourt($rowBug['application']);
        if (!$application){
          throw new sfException('Erreur extraction des bugs (mapping application)');
        }
        $bug->setApplication($application);
        $bug->setNumeroMantis($rowBug['numero']);
        $bug->setCategorie($rowBug['categorie']);
        $bug->setSeverite($rowBug['severite']);
        $bug->setStatut($rowBug['statut']);
        $bug->setPortee($rowBug['view_state']);
        $bug->setResume(substr($rowBug['resume'],0,254));
        $bug->setPriorite($rowBug['priorite']);
        $bug->setVersionResolution($rowBug['version']);
        $bug->save();

        try{
          $this->extractCriteres($bug);
          $this->extractHisto($bug);
        }
        catch(Exception $e){
          throw new sfException('Erreur extraction des bugs ('.$e->getMessage().')');
          //rollback ?
        }
        //commit ?
      }
    }
    else{
      throw new sfException('Erreur extraction des bugs (inconnue bug)');
    }
  }

  /**
   * Extrait les critères spécifiques et leurs valeurs d'un bug
   *
   * @param Bug $bug
   * @throws sfException
   */
  private function extractCriteres(Bug $bug)
  {
    $bug_id = $bug->getNumeroMantis();
    if (!$bug_id){
      throw new sfException('Erreur extraction des bugs (numero mantis pour critere)');
    }

    //Critere pour ce bug
    $stmtCritere = $this->dbh->prepare(self::QUERY_CRITERE);
    $stmtCritere->bindParam(':bug_id', $bug_id);

    if ($stmtCritere->execute()){
      while($rowCritere = $stmtCritere->fetch()){
        if ($rowCritere['value'] != "") {
          $critere = new Critere();
          $critere->setBug($bug);
          $critere->setLibelle($rowCritere['name']);
          $critere->setValeur($rowCritere['value']);
          $critere->save();
        }
      }
    }
    else{
      throw new sfException('Erreur extraction des bugs (inconnue critere)');
    }
  }

  /**
   * Extrait l'historique d'un bug
   *
   * @param Bug $bug
   * @throws sfException
   */
  private function extractHisto(Bug $bug)
  {
    $bug_id = $bug->getNumeroMantis();
    if (!$bug_id){
      throw new sfException('Erreur extraction des bugs (numero mantis pour historique)');
    }

    //Historique pour ce bug
    $stmtHisto = $this->dbh->prepare(self::QUERY_HISTORIQUE);
    $stmtHisto->bindParam(':bug_id', $bug_id);

    if ($stmtHisto->execute()){
      $i = 0;
      $tabHisto = null;
      while($rowHisto = $stmtHisto->fetch()){
        $tabHisto[$i]['old_value'] = $rowHisto['old_value'] != '' ? $rowHisto['old_value'] : 0;
        $tabHisto[$i]['new_value'] = $rowHisto['new_value'] != '' ? $rowHisto['new_value'] : 0;
        $tabHisto[$i]['date_modified'] = date("Y-m-d H:i:s", $rowHisto['date_modified']);
        $tabHisto[$i]['date_sans_heure'] = date("Y-m-d", $rowHisto['date_modified']);
        $i = $i + 1;
      }
      //FIXME A factoriser
      $bugEstOuvert = false;
      $bugEstResolu = false;
      $bugEstLivre = false;
      $bugEstFerme = false;

      for($i = 0; $i < count($tabHisto); $i++) {
        $his_flag = "";

        $isReouvert = (intval($tabHisto[$i]['old_value']) == self::CODE_STATUT_LIVRE && intval($tabHisto[$i]['new_value']) < self::CODE_STATUT_LIVRE);
        $isRegression = (intval($tabHisto[$i]['old_value']) == self::CODE_STATUT_FERME && intval($tabHisto[$i]['new_value']) < self::CODE_STATUT_FERME);

        //Cas de réouverture d'un bug livré
        if ($isReouvert || $isRegression){
          if ($isReouvert){
            //Historique à fermer
            $histo = new Historique();
            $histo->setBug($bug);
            $histo->setBugStatut(self::CODE_STATUT_FERME);
            $histo->setDateDebut($tabHisto[$i]['date_modified']);
            $histo->setFlag('F');
            $histo->save();
          }

          //Création d'un nouveau bug avec non corrige = à  l'id du bug initial
          $nouveauBug = $bug->copy();
          if ($isReouvert){
            $nouveauBug->setNonCorrige($bug->getId());
          }
          else{
            $nouveauBug->setRegression($bug->getId());
          }
          $nouveauBug->save();

          //Historique pour le nouveau bug (flag ouverture)
          $histoNouveauBug = new Historique();
          $histoNouveauBug->setBug($nouveauBug);
          $histoNouveauBug->setDateDebut($tabHisto[$i]['date_modified']);
          if (array_key_exists($i+1, $tabHisto)){
            $histoNouveauBug->setDateFin($tabHisto[$i+1]['date_modified']);
          }
          $histoNouveauBug->setFlag("O");
          $histoNouveauBug->save();
        }

        //Cas d'ouverture d'un bug (pas déjà ouvert et état détecté)
        if (!$bugEstOuvert && intval($tabHisto[$i]['new_value']) == self::CODE_STATUT_DETECTE) {
          //Historique à ouvert
          $histo = new Historique();
          $histo->setBug($bug);
          $histo->setBugStatut(self::CODE_STATUT_DETECTE);
          $histo->setDateDebut($tabHisto[$i]['date_modified']);
          if (array_key_exists($i+1, $tabHisto)){
            $histo->setDateFin($tabHisto[$i+1]['date_modified']);
          }
          $histo->setFlag('O');
          $histo->save();

          //Doublons de cas d'ouverture
          $bugEstOuvert = true;
        }

        //Cas de résolution d'un bug
        if (intval($tabHisto[$i]['new_value']) == self::CODE_STATUT_RESOLU) {
          //Historique à resolu
          $histo = new Historique();
          $histo->setBug($bug);
          $histo->setBugStatut(self::CODE_STATUT_RESOLU);
          $histo->setDateDebut($tabHisto[$i]['date_modified']);
          if (array_key_exists($i+1, $tabHisto)){
            $histo->setDateFin($tabHisto[$i+1]['date_modified']);
          }
          $histo->setFlag('R');
          $histo->save();

          //Succession des états
          $bugEstResolu = true;
        }

        //Cas de livraison d'un bug
        if (intval($tabHisto[$i]['new_value']) == self::CODE_STATUT_LIVRE) {
          //Historique à livre
          $histo = new Historique();
          $histo->setBug($bug);
          $histo->setBugStatut(self::CODE_STATUT_LIVRE);
          $histo->setDateDebut($tabHisto[$i]['date_modified']);
          if (array_key_exists($i+1, $tabHisto)){
            $histo->setDateFin($tabHisto[$i+1]['date_modified']);
          }

          //Le bug est il déjà résolu
          $flag = !$bugEstResolu ? 'R,L' : 'L';
          $histo->setFlag($flag);
          $histo->save();

          //Succession des états
          $bugEstResolu = true;
          $bugEstLivre = true;
        }

        //Cas de fermeture d'un bug
        if (intval($tabHisto[$i]['new_value']) == self::CODE_STATUT_FERME) {
          //Historique à fermer
          $histo = new Historique();
          $histo->setBug($bug);
          $histo->setBugStatut(self::CODE_STATUT_FERME);
          $histo->setDateDebut($tabHisto[$i]['date_modified']);
          if (array_key_exists($i+1, $tabHisto)){
            $histo->setDateFin($tabHisto[$i+1]['date_modified']);
          }

          //Le bug est il déjà résolu
          $flag = '';
          if($bugEstResolu){
            if ($bugEstLivre){
              $flag = 'F';
            }
            else{
              $flag = 'L,F';
            }
          }
          else{
            if ($bugEstLivre){
              //Impossible !
              $flag = '';
            }
            else{
              $flag = 'R,L,F';
            }
          }
          $histo->setFlag($flag);
          $histo->save();

          //Succession des états
          $bugEstResolu = true;
          $bugEstLivre = true;
          $bugEstFerme = true;
        }
      }
    }
    else{
      throw new sfException('Erreur extraction des bugs (inconnue histo)');
    }
  }

  /**
   * FROM STACK OVERFLOW, THANKS ;)
   *
   * Replaces any parameter placeholders in a query with the value of that
   * parameter. Useful for debugging. Assumes anonymous parameters from
   * $params are are in the same order as specified in $query
   *
   * A utiliser pour le mode dégradé.
   *
   * @param string $query The sql query with parameter placeholders
   * @param array $params The array of substitution parameters
   * @return string The interpolated query
   */
  public static function interpolateQuery($query, $params) {
    $keys = array();

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }
    }

    $query = preg_replace($keys, $params, $query, 1, $count);

    #trigger_error('replaced '.$count.' keys');

    return $query;
  }

}